package fr.labri.macchiato.feature.distantAccess;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.xml.client.Document;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.xml.client.XMLParser;
import com.smartgwt.client.util.JSON;

import fr.labri.macchiato.core.api.AbstractFinder;
import fr.labri.macchiato.core.model.BusinessEntity;
import fr.labri.macchiato.core.model.Article;
import fr.labri.macchiato.core.model.Location;
import fr.labri.macchiato.core.model.Offer;
import fr.labri.macchiato.core.model.Product;
import fr.labri.macchiato.core.model.utils.Address;
import fr.labri.macchiato.core.technical.Callback;
import fr.labri.macchiato.core.technical.FrameworkException;


/**
 * A Class that permits to choose the best offer of a product by checking user's
 * preferences order and using SparQl request
 * 
 * @author Abdoulaye DIALLO
 * 
 * 
 */
public class RestProductFinder extends AbstractFinder {

	private RestQueryLauncher queryLauncher;
	String getQuery;

	/**
	 * Default Constructor
	 */
	public RestProductFinder(String url, String name, String urlIcon) {
		super(url, name, urlIcon);
		queryLauncher = new RestQueryLauncher(getUrl());
	}

	public void getCheaperOffer(final Product product,
			final Callback<Product> callback) {

		findCorrespondingOffers(product,
				new Callback<List<Offer>>() {

					@Override
					public void onCallback(List<Offer> result) {

						Product bestOffer = product;
							if (!result.isEmpty()) {
								bestOffer = result.get(0);
								bestOffer.setQuantity(product.getQuantity());
							}
						callback.onCallback(bestOffer);

					}
				});

	}

	/**
	 * Find all the corresponding offers of the Product passed as parameter. The
	 * research will be based on the UserPreferences. Actually we don't care
	 * about the complicated cases.
	 */
	private void findCorrespondingOffers(Product product,
			final Callback<List<Offer>> callback) {

		String searchKey = product.getName();

		String searchQuery = RestQueryFactory.getInstance().getCheaperOfferQuery(
				searchKey);

		queryLauncher.launchQueryForJSON(searchQuery,
				new AsyncCallback<String>() {

					public void onSuccess(String result) {

						List<Offer> offers = decodeOffer(result);
						callback.onCallback(offers);
					}

					public void onFailure(Throwable caught) {
						caught.printStackTrace();
						throw new FrameworkException(
								"Échec lors de la recherche");
					}
				});
	}


	public void getArticles(String keyword, final Callback<List<Article>> callback) {
		String query = RestQueryFactory.getInstance().searchModels(keyword);
		System.out.println("ok1");
		queryLauncher.launchQueryForJSON(query, new AsyncCallback<String>() {
			public void onSuccess(String result) {
				System.out.println("ok3");
				System.out.println(result);
				
				List<Article> models = decodeModel(result);
				System.out.println("ok4");
				callback.onCallback(models);
				System.out.println("ok5");
			}

			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
				caught.printStackTrace();
			}
		});
		System.out.println("ok2");
	}


	public void getOffers(String ean, List<BusinessEntity> providers, final Callback<List<Offer>> callback) {
		String query = RestQueryFactory.getInstance().getOffersByEanQuery(ean, providers);
		System.out.println(query);
		queryLauncher.launchQueryForJSON(query, new AsyncCallback<String>() {

			public void onSuccess(String result) {

				List<Offer> offers = decodeOffer(
						result);
				callback.onCallback(offers);
			}

			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
			}
		});

	}
	
	
	public void getBusinessEntities(
			final Callback<List<BusinessEntity>> callback) {
		getQuery = RestQueryFactory.getInstance().retrieveAllInfosOnEntities();
		
		queryLauncher.launchQueryForJSON(getQuery, new AsyncCallback<String>() {

			public void onFailure(Throwable caught) {
				caught.printStackTrace();
				throw new FrameworkException(
						"Echec lors de la récuperation du nom + page + logo des groupes");
			}

			public void onSuccess(String result) {
				List<BusinessEntity> details = decodeBusinessEntity(result);
				callback.onCallback(details);

			}

		});

	}
	
	public List<BusinessEntity> decodeBusinessEntity(String jsonString) {
        List<BusinessEntity> results = new ArrayList<BusinessEntity>();

        JSONObject result = new JSONObject(JSON.decode(jsonString));
        result = result.get("results").isObject();

        JSONArray bindings = result.get("bindings").isArray();

        for (int i = 0; i < bindings.size(); i++) {

                String legalName = getAsString(bindings, i, "legalName");

                String logo = getAsString(bindings, i, "depiction");

                String page = getAsString(bindings, i, "page");
                
                String location = getAsString(bindings, i, "location");
                

                BusinessEntity entrySet = new BusinessEntity(legalName, logo, page);
                entrySet.addLocation(new Location(location, new Address("", "", "", "")));
                results.add(entrySet);
                

        }
        return results;
	}
	
	
	private List<Offer> decodeOffer(String jsonString) {

        List<Offer> offers = new ArrayList<Offer>();

        JSONObject result = new JSONObject(JSON.decode(jsonString));
        result = result.get("results").isObject();

        JSONArray bindings = result.get("bindings").isArray();
        for (int i = 0; i < bindings.size(); i++) {
                
                String ean = getAsString(bindings, i, "ean");
                String name = getAsString(bindings, i, "name");
                String priceString = getAsString(bindings, i, "price");
                double price = Double.valueOf(priceString);
                String providerName = getAsString(bindings, i, "provider");

                BusinessEntity provider = new BusinessEntity(providerName);
                Article model = new Article(ean, name);
                Offer offer = new Offer(model, provider, price);

                offers.add(offer);
        }
        return offers;
	}
	
	private List<Article> decodeModel(String string) {
		List<Article> result = new ArrayList<Article>();
		
		JSONObject jsObject = new JSONObject(JSON.decode(string));
		JSONObject jsResults = jsObject.get("results").isObject();
		JSONArray bindings = jsResults.get("bindings").isArray();

		for (int i = 0; i < bindings.size(); i++) {
			
			String ean = getAsString(bindings, i, "ean");
			String name = getAsString(bindings, i, "name");
            Article model = new Article(ean, name);
            result.add(model);
		}
		return result;
		
		
		
//		Document doc = XMLParser.parse(string);
/*	    NodeList list = doc.getElementsByTagName(modelType.getRecordName());
	    ArrayList<ModelData> records = new ArrayList<ModelData>();
	    for (int i = 0; i < list.getLength(); i++) {
	      Node node = list.item(i);
	      Element elem = (Element) node;
	      ModelData model = newModelInstance();
	      for (int j = 0; j < modelType.getFieldCount(); j++) {
	        DataField field = modelType.getField(j);
	        Class type = field.getType();
	        String name = field.getName();
	        String map = field.getMap() != null ? field.getMap() : field.getName();
	        String v = getValue(elem, map);
	 
	        if (type != null) {
	          if (type.equals(Integer.class)) {
	            model.set(name, Integer.parseInt(v));
	          } else if (type.equals(Long.class)) {
	            model.set(name, Long.parseLong(v));
	          } else if (type.equals(Float.class)) {
	            model.set(name, Float.parseFloat(v));
	          } else if (type.equals(Double.class)) {
	            model.set(name, Double.parseDouble(v));
	          } else if (type.equals(Date.class)) {
	            if (field.getFormat().equals("timestamp")) {
	              Date d = new Date(Long.parseLong(v) * 1000);
	              model.set(name, d);
	            } else {
	              DateTimeFormat format = DateTimeFormat.getFormat(field.getFormat());
	              Date d = format.parse(v);
	              model.set(name, d);
	            }
	          }
	        } else {
	          model.set(field.getName(), v);
	        }
	 
	      }
	      records.add(model);
	    }
	 
	    int totalCount = records.size();
	    Node root = doc.getElementsByTagName(modelType.getRoot()).item(0);
	    if (root != null && modelType.getTotalName() != null) {
	      String tot = getValue((Element)root, modelType.getTotalName());
	      if (tot != null) {
	        totalCount = Integer.parseInt(tot);
	      }
	    }*/
	}
	
	
	private String getAsString(JSONArray bindings, int index, String key) {
		JSONString string = bindings.get(index).isObject().get(key).isObject().get("value").isString();
		
		return (string == null) ? null : string.stringValue();
	}

//	public String getName() {
//		return "Macchiato.fr";
//	}
//
//	public String getUrlIcon() {
//		return "http://www.transnationale.org/upload/carrefour3.gif";
//	}

}
